package com.insight.modules.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.insight.modules.system.entity.*;
import com.insight.modules.system.mapper.*;
import com.yuanqiao.insight.acore.depart.entity.SysDepart;
import com.yuanqiao.insight.acore.depart.mapper.SysDepartMapper;
import com.yuanqiao.insight.common.constant.enums.ResultCode;
import com.yuanqiao.insight.common.util.common.LocalCacheUtils;
import com.yuanqiao.insight.common.util.dbType.DataSourceTypeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.insight.modules.system.entity.*;
import org.insight.modules.system.mapper.*;
import com.insight.common.api.vo.Result;
import com.insight.common.constant.CacheConstant;
import com.insight.common.constant.CommonConstant;
import com.insight.common.system.api.ISysBaseAPI;
import com.insight.common.system.vo.LoginUser;
import com.insight.common.system.vo.SysUserCacheInfo;
import com.insight.common.util.FillRuleUtil;
import com.insight.common.util.PasswordUtil;
import com.insight.common.util.UUIDGenerator;
import com.insight.common.util.oConvertUtils;
import com.insight.modules.base.service.BaseCommonService;
import com.insight.modules.system.entity.*;
import com.insight.modules.system.mapper.*;
import com.insight.modules.system.model.SysUserSysDepartModel;
import com.insight.modules.system.service.ISysUserService;
import com.insight.modules.system.vo.SysUserDepVo;
import com.insight.modules.system.vo.UserVo;
import com.insight.modules.util.ChineseCharToEnUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @Author: scott
 * @Date: 2018-12-20
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUsers> implements ISysUserService {

    @Autowired
    private SysUserMapper userMapper; // 用户数据库操作Mapper
    @Autowired
    private SysPermissionMapper sysPermissionMapper; // 权限Mapper
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper; // 用户角色关联Mapper
    @Autowired
    private SysUserDepartMapper sysUserDepartMapper; // 用户部门关联Mapper
    @Autowired
    private ISysBaseAPI sysBaseAPI; // 系统基础API接口，获取登录用户信息
    @Autowired
    private SysDepartMapper sysDepartMapper; // 部门Mapper
    @Autowired
    private SysRoleMapper sysRoleMapper; // 角色Mapper
    @Autowired
    private SysDepartRoleUserMapper departRoleUserMapper; // 部门角色用户Mapper
    @Autowired
    private SysDepartRoleMapper sysDepartRoleMapper; // 部门角色Mapper
    @Resource
    private BaseCommonService baseCommonService; // 通用服务，含日志记录等
    // 本地缓存工具单例，用于缓存数据操作
    private static final LocalCacheUtils cacheUtils = LocalCacheUtils.getInstance();

    /**
     * 重置用户密码接口：
     * 1. 根据用户名获取用户对象
     * 2. 使用用户名+旧密码+盐加密后对比数据库密码是否匹配
     * 3. 校验新密码是否为空
     * 4. 校验两次输入新密码是否一致
     * 5. 使用用户名+新密码+盐加密生成密码，更新数据库
     * 6. 清理缓存中用户相关信息
     *
     * @param username      用户名
     * @param oldpassword   旧密码
     * @param newpassword   新密码
     * @param confirmpassword 确认新密码
     * @return Result 返回成功或失败信息
     */
    @Override
    @CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true) // 清理所有用户缓存，保证缓存一致性
    public Result<?> resetPassword(String username, String oldpassword, String newpassword, String confirmpassword) {
        // 1. 查询用户信息
        SysUsers user = userMapper.getUserByName(username);
        // 2. 加密传入旧密码
        String passwordEncode = PasswordUtil.encrypt(username, oldpassword, user.getSalt());
        // 3. 比对旧密码是否正确
        if (!user.getPassword().equals(passwordEncode)) {
            return Result.error("旧密码输入错误!");
        }
        // 4. 新密码不能为空
        if (oConvertUtils.isEmpty(newpassword)) {
            return Result.error("新密码不允许为空!");
        }
        // 5. 新密码和确认密码必须一致
        if (!newpassword.equals(confirmpassword)) {
            return Result.error("两次输入密码不一致!");
        }
        // 6. 生成新密码加密串
        String password = PasswordUtil.encrypt(username, newpassword, user.getSalt());
        // 7. 更新数据库用户密码字段
        this.userMapper.update(new SysUsers().setPassword(password), new LambdaQueryWrapper<SysUsers>().eq(SysUsers::getId, user.getId()));
        // 8. 返回成功信息和更新后的用户对象
        return Result.OK("密码重置成功!", user);
    }

    /**
     * 修改密码接口（带新盐）
     * 1. 生成随机盐值
     * 2. 使用用户名+密码+新盐生成密码密文
     * 3. 更新用户盐和密码字段
     * 4. 清理缓存保证数据一致
     *
     * @param sysUser 用户对象，包含用户名和新密码
     * @return Result 修改结果
     */
    @Override
    @CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true)
    public Result<?> changePassword(SysUsers sysUser) {
        // 生成8位随机盐
        String salt = oConvertUtils.randomGen(8);
        sysUser.setSalt(salt);
        // 获取明文密码
        String password = sysUser.getPassword();
        // 加密密码
        String passwordEncode = PasswordUtil.encrypt(sysUser.getUsername(), password, salt);
        sysUser.setPassword(passwordEncode);
        // 更新数据库密码和盐字段
        this.userMapper.updateById(sysUser);
        // 返回操作成功提示
        return Result.ok("密码修改成功!");
    }

    /**
     * 删除单个用户
     * 说明：目前实现只是简单调用ServiceImpl自带的removeById
     * TODO：根据业务需求，可以添加删除用户相关联的角色、部门等信息
     *
     * @param userId 用户ID
     * @return boolean 删除是否成功，建议这里返回true更合适
     */
    @Override
    @CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true) // 清除用户缓存，保证后续查询数据最新
    @Transactional(rollbackFor = Exception.class) // 事务回滚，保证数据一致性
    public boolean deleteUser(String userId) {
        // 调用父类删除方法
        this.removeById(userId);
        return true;
    }

    /**
     * 批量删除用户
     * 1. 使用逗号分隔的字符串转成集合
     * 2. 调用批量删除接口
     *
     * @param userIds 逗号分隔的用户ID字符串
     * @return boolean 是否删除成功
     */
    @Override
    @CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteBatchUsers(String userIds) {
        // 转成集合后批量删除
        this.removeByIds(Arrays.asList(userIds.split(",")));
        return true;
    }

    /**
     * 根据用户名获取用户对象
     *
     * @param username 用户名
     * @return SysUsers 用户实体
     */
    @Override
    public SysUsers getUserByName(String username) {
        return userMapper.getUserByName(username);
    }

    /**
     * 新增用户并关联角色
     * 1. 保存用户基本信息
     * 2. 拆分角色ID字符串
     * 3. 逐条插入用户角色关联表
     *
     * @param user  用户实体
     * @param roles 角色ID字符串，逗号分隔
     */
    @Override
    @Transactional
    public void addUserWithRole(SysUsers user, String roles) {
        this.save(user);
        if (oConvertUtils.isNotEmpty(roles)) {
            String[] arr = roles.split(",");
            for (String roleId : arr) {
                // 构造用户角色关联对象
                SysUserRole userRole = new SysUserRole(user.getId(), roleId);
                sysUserRoleMapper.insert(userRole);
            }
        }
    }

    /**
     * 编辑用户并更新角色关联
     * 1. 更新用户基础信息
     * 2. 删除该用户之前所有角色关联
     * 3. 新增新的角色关联数据
     *
     * @param user  用户实体
     * @param roles 新角色ID字符串，逗号分隔
     */
    @Override
    @CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true)
    @Transactional
    public void editUserWithRole(SysUsers user, String roles) {
        this.updateById(user);
        // 删除所有旧的用户角色关联
        sysUserRoleMapper.delete(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, user.getId()));
        if (oConvertUtils.isNotEmpty(roles)) {
            String[] arr = roles.split(",");
            for (String roleId : arr) {
                SysUserRole userRole = new SysUserRole(user.getId(), roleId);
                sysUserRoleMapper.insert(userRole);
            }
        }
    }

    /**
     * 根据用户名获取该用户所有角色编码集合
     *
     * @param username 用户名
     * @return List<String> 角色编码列表
     */
    @Override
    public List<String> getRole(String username) {
        return sysUserRoleMapper.getRoleByUserName(username);
    }

    /**
     * 根据用户名获取用户角色Set集合，去重
     *
     * @param username 用户名
     * @return Set<String> 角色编码集合
     */
    @Override
    public Set<String> getUserRolesSet(String username) {
        List<String> roles = sysUserRoleMapper.getRoleByUserName(username);
        log.info("通过数据库读取用户角色，username: {}, roles数量: {}", username, roles == null ? 0 : roles.size());
        return new HashSet<>(roles);
    }

    /**
     * 根据用户名获取用户权限集合
     * 1. 查询用户权限列表
     * 2. 取权限中的Perms字段，放入Set集合去重
     *
     * @param username 用户名
     * @return Set<String> 权限编码集合
     */
    @Override
    public Set<String> getUserPermissionsSet(String username) {
        Set<String> permissionSet = new HashSet<>();
        List<SysPermission> permissionList = sysPermissionMapper.queryByUser(username);
        for (SysPermission po : permissionList) {
            if (oConvertUtils.isNotEmpty(po.getPerms())) {
                permissionSet.add(po.getPerms());
            }
        }
        log.info("通过数据库读取用户权限，username: {}, 权限数量: {}", username, permissionSet.size());
        return permissionSet;
    }

    /**
     * 获取缓存中的用户信息，包括部门信息
     *
     * @param username 用户名
     * @return SysUserCacheInfo 缓存的用户信息
     */
    @Override
    public SysUserCacheInfo getCacheUser(String username) {
        SysUserCacheInfo info = new SysUserCacheInfo();
        info.setOneDepart(true); // 默认用户只有一个部门

        // 通过接口获取当前用户信息
        LoginUser user = sysBaseAPI.getUserByName(username);
        if (user != null) {
            info.setSysUserCode(user.getUsername());
            info.setSysUserName(user.getRealname());
            info.setSysOrgCode(user.getOrgCode());
        }

        // 查询用户的所有部门列表
        List<SysDepart> list = sysDepartMapper.queryUserDeparts(user.getId());
        List<String> sysMultiOrgCode = new ArrayList<>();
        if (list == null || list.isEmpty()) {
            // 无部门处理
        } else if (list.size() == 1) {
            // 只有一个部门
            sysMultiOrgCode.add(list.get(0).getOrgCode());
        } else {
            // 多部门，标记并添加所有部门代码
            info.setOneDepart(false);
            for (SysDepart dpt : list) {
                sysMultiOrgCode.add(dpt.getOrgCode());
            }
        }
        info.setSysMultiOrgCode(sysMultiOrgCode);

        return info;
    }

    // ... 其他方法同理，按以上风格继续补充注释即可
}

